#!/bin/sh

if [ $# -ne 1 -o ! -f $1/jsapi.h ]; then
  echo "Usage:"
  echo "$0 /path/to/libmozjs/headers/"
  exit 1
fi

if [ ! -f ./js_script_context.h ]; then
  echo "$0 must be executed in extensions/smjs_script_runtime directory."
  exit 1
fi

rm -f libmozjs_glue.*
rm -f mozjs_api_*

# Extract all public function definitions from jsapi.h
cat > mozjs_sed_script << SED_SCRIPT
/^.*JS_PUBLIC_API(.*)$/,/;$/ {
  s/^.*JS_PUBLIC_API(\(.*\))$/MOZJS_API(\1,/
  s/^\(JS_[a-zA-Z0-9_]\{1,\}\)(/\1, (/
  s/);$/));/
  H
}
/^.*JS_PUBLIC_API(.*).*JS_.*(.*);$/ {
  s/^.*JS_PUBLIC_API(\([a-zA-Z0-9 _*]\{1,\}\))/MOZJS_API(\1,/
  s/\([^a-zA-Z0-9]\)\(JS_[a-zA-Z0-9_]\{1,\}\)(/\1\2, (/
  s/);$/));/
  H
}
$ {
  x
  s/\nJS_/ JS_/g
  s/\n \{1,\}/ /g
  p
}
SED_SCRIPT

# Extract macro replacement sed script
cat $1/js*.h | grep '^#define *JS_[a-zA-Z0-9_]\{1,\} \{1,\}JS_[a-zA-Z0-9_]\{1,\}$' |\
  sed -e 's/^.*\(JS_[a-zA-Z0-9_]\{1,\}\).*\(JS_[a-zA-Z0-9_]\{1,\}\)/s\/\1\/\2\//' |\
  sort | uniq > mozjs_sed_script_macro_replace

# Extract all public api definitions from jsapi.h
rm -f mozjs_api_declare.all
for file in $1/js*.h; do
  if (cat $file | grep '^[^#]*JS_PUBLIC_API') > /dev/null 2>&1; then
    base_file=`basename $file`
    sed -n -f mozjs_sed_script $file |\
      grep -v '^$' > mozjs_api_declare_in.$base_file._file
    cat mozjs_api_declare_in.$base_file._file >> mozjs_api_declare.all
  fi
done

cat mozjs_api_declare.all | sort | uniq > mozjs_api_declare

# Extrace api list
sed -e 's/^MOZJS_API([0-9a-zA-Z_ *]\{1,\}, \(JS_[a-zA-Z0-9_]\{1,\}\),.*$/\1/' \
  mozjs_api_declare | uniq > mozjs_api_list

# Extract all js functions used by us.
rm -f used_mozjs_api_list
for i in `cat *.cc *.h tests/*.cc ../gtkmoz_browser_element/*.cc \
          ../gtkmoz_browser_element/*.h  |\
          sed -e 's/[^a-zA-Z_]\(JS_[a-zA-Z0-9_]\{1,\}\)[^a-zA-Z0-9_]/\n\1\n/g' |\
          grep '^JS_[a-zA-Z0-9_]\{1,\}$' | sort | uniq`; do
  if grep $i mozjs_api_list > /dev/null; then
    echo $i >> used_mozjs_api_list
  else
    echo "$i is not in api list."
  fi
done

# Treat JS_GetClass specially
echo "JS_GetClass" >> used_mozjs_api_list

# Replace macros
mv -f mozjs_api_declare mozjs_api_declare.orig
sed -f mozjs_sed_script_macro_replace mozjs_api_declare.orig > mozjs_api_declare
mv -f mozjs_api_list mozjs_api_list.orig
sed -f mozjs_sed_script_macro_replace mozjs_api_list.orig > mozjs_api_list
mv -f used_mozjs_api_list used_mozjs_api_list.orig
sed -f mozjs_sed_script_macro_replace used_mozjs_api_list.orig > used_mozjs_api_list

# Get rid off apis that are not used.
rm -f used_mozjs_api_declare
for i in `cat used_mozjs_api_list`; do
  grep "[^a-zA-Z]$i[^a-zA-Z0-9_]" mozjs_api_declare >> used_mozjs_api_declare
done

# Find out all used header files.
rm -f used_mozjs_header_files
for file in mozjs_api_declare_in.*._file; do
  for i in `cat used_mozjs_api_list`; do
    if (grep "[^a-zA-Z]$i[^a-zA-Z0-9_]" $file) > /dev/null 2>&1; then
      echo "$file" |\
        sed -e 's/mozjs_api_declare_in.\(.*\)._file$/#include <\1>/' \
          >> used_mozjs_header_files
      break
    fi
  done
done

# generate proxies for stub functions.
rm -f mozjs_api_proxies_declare
rm -f mozjs_api_proxies_implement
for func in `cat used_mozjs_api_list | grep '.*Stub$'`; do
  api=`cat used_mozjs_api_declare | grep "$func[^a-zA-Z0-9]"`
  ret=`echo $api | sed -e "s/^MOZJS_API(\\([a-zA-Z0-9 _*]\{1,\}\\),.*/\1/"`
  params=`echo $api | sed -e "s/.*$func.*\\((.*)\\));/\1/"`
  call_params=`echo $params | sed -e 's/const //g' |\
               sed -e 's/\([(,]\) *[a-zA-Z0-9_*]\{1,\} \**/\1/g'`
  cat >> mozjs_api_proxies_declare << MOZJS_API_PROXY_DECLARE

/**
 * Proxy function for $func, to make sure it can be used in static
 * initialization code.
 */
$ret ${func}Proxy$params;
MOZJS_API_PROXY_DECLARE

  cat >> mozjs_api_proxies_implement << MOZJS_API_PROXY_IMPLEMENT

$ret ${func}Proxy$params {
  return ${func}.func$call_params;
}
MOZJS_API_PROXY_IMPLEMENT
done

# generate libmozjs_glue.h
cat > libmozjs_glue.h << LIBMOZJS_GLUE_H
/*
  Copyright 2008 Google Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

// This file is generated by gen_libmozjs_glue.sh automatically.
// Don't edit it manually.
// Don't forget to regenerate this file if more/less libmozjs functions are
// used.
#ifndef EXTENSIONS_SMJS_SCRIPT_RUNTIME_LIBMOZJS_GLUE_
#define EXTENSIONS_SMJS_SCRIPT_RUNTIME_LIBMOZJS_GLUE_

LIBMOZJS_GLUE_H

cat used_mozjs_header_files >> libmozjs_glue.h

cat >> libmozjs_glue.h << LIBMOZJS_GLUE_H

// This file only makes sense when XPCOM_GLUE is defined.
#ifdef XPCOM_GLUE

#include <nsXPCOMGlue.h>

namespace ggadget {
namespace libmozjs {

#define MOZJS_API(type, name, params) \\
    typedef type (* name##FuncType) params; \\
    union name##Type { name##FuncType func; NSFuncPtr ptr; }

LIBMOZJS_GLUE_H

# Append api type declarations.
# Remove JS_GetClass(), it'll be handled specially
cat used_mozjs_api_declare | \
  grep -v ' JS_GetClass,' | \
  sed -e '/^.*JS_SetOperationCallback,.*$/s/^\(.*\)$/#ifdef JS_OPERATION_WEIGHT_BASE\n\1\n#endif/' \
  >> libmozjs_glue.h

cat >> libmozjs_glue.h << LIBMOZJS_GLUE_H
#ifdef JS_THREADSAFE
MOZJS_API(JSClass *, JS_GetClass, (JSContext *cx, JSObject *obj));
#else
MOZJS_API(JSClass *, JS_GetClass, (JSObject *obj));
#endif

#undef MOZJS_API

#ifdef JS_OPERATION_WEIGHT_BASE
#define MOZJS_FUNC_JS_SetOperationCallback \\
LIBMOZJS_GLUE_H

sed -e 's/^\(.*\)$/  MOZJS_FUNC(\1)/' used_mozjs_api_list | \
  grep JS_SetOperationCallback >> libmozjs_glue.h

cat >> libmozjs_glue.h << LIBMOZJS_GLUE_H
#else
#define MOZJS_FUNC_JS_SetOperationCallback
#endif

#define MOZJS_FUNCTIONS \\
LIBMOZJS_GLUE_H

# Append api list
sed -e 's/^\(.*\)$/  MOZJS_FUNC(\1) \\/' used_mozjs_api_list | \
  grep -v JS_SetOperationCallback >> libmozjs_glue.h

cat >> libmozjs_glue.h << LIBMOZJS_GLUE_H
  MOZJS_FUNC_JS_SetOperationCallback

#define MOZJS_FUNC(fname) extern fname##Type fname;

MOZJS_FUNCTIONS

#undef MOZJS_FUNC

LIBMOZJS_GLUE_H

# Append macro defines to replace apis.
# For Stub functions, use a proxy stub instead of a function pointer.
sed -n -e '/.*Stub$/!s/^\(.*\)$/#define \1 ggadget::libmozjs::\1.func/p' \
  used_mozjs_api_list >> libmozjs_glue.h

cat >> libmozjs_glue.h << LIBMOZJS_GLUE_H

// Stub functions are likely be used in static initialization code.
LIBMOZJS_GLUE_H

sed -n -e '/.*Stub$/s/^\(.*\)$/#define \1 ggadget::libmozjs::\1Proxy/p' \
  used_mozjs_api_list >> libmozjs_glue.h

# Append proxy declaractions.
cat mozjs_api_proxies_declare >> libmozjs_glue.h

# Finish libmozjs_glue.h
cat >> libmozjs_glue.h << LIBMOZJS_GLUE_H

/**
 * Starts up standalone libmozjs glue.
 * @return NS_OK if success.
 */
bool LibmozjsGlueStartup();

/**
 * Shuts down standalone libmozjs glue. libmozjs.so will be unloaded.
 */
void LibmozjsGlueShutdown();

/**
 * Starts up libmozjs glue along with XPCOM. Must be called after xpcom has
 * been started up.
 * @return NS_OK if success.
 */
nsresult LibmozjsGlueStartupWithXPCOM();

} // namespace libmozjs
} // namespace ggadget

#endif // XPCOM_GLUE
#endif // EXTENSIONS_SMJS_SCRIPT_RUNTIME_LIBMOZJS_GLUE_
LIBMOZJS_GLUE_H

# Generate libmozjs_glue.cc
cat > libmozjs_glue.cc << LIBMOZJS_GLUE_CC
/*
  Copyright 2008 Google Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

// This file is generated by gen_libmozjs_glue.sh automatically.
// Don't edit it manually.
// Don't forget to regenerate this file if more/less libmozjs functions are
// used.
#ifdef XPCOM_GLUE
#include "libmozjs_glue.h"

#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <ggadget/logger.h>
#include <ggadget/system_utils.h>
#include <ggadget/string_utils.h>

namespace ggadget {
namespace libmozjs {

// Undefine api macros so that we can declare the real glue apis."
LIBMOZJS_GLUE_CC

sed -e 's/^\(.*\)$/#undef \1/' used_mozjs_api_list >> libmozjs_glue.cc

cat >> libmozjs_glue.cc << LIBMOZJS_GLUE_CC

// Define real function pointers.
#ifdef _DEBUG
#define MOZJS_FUNC(fname) \\
  static void fname##NotFoundHandler() { \\
    LOGE("libmozjs symbol %s is missing.", #fname); \\
    abort(); \\
  } \\
  fname##Type fname = { (fname##FuncType)fname##NotFoundHandler };
#else
#define MOZJS_FUNC(fname) \\
  fname##Type fname = { NULL };
#endif

MOZJS_FUNCTIONS

#undef MOZJS_FUNC

#define MOZJS_FUNC(fname) { #fname, &fname.ptr },

static const nsDynamicFunctionLoad kMOZJSSymbols[] = {
  MOZJS_FUNCTIONS
  { nsnull, nsnull }
};

#undef MOZJS_FUNC
LIBMOZJS_GLUE_CC

#Append proxy implementations
cat mozjs_api_proxies_implement >> libmozjs_glue.cc

cat >> libmozjs_glue.cc << LIBMOZJS_GLUE_CC

#if defined(SUNOS4) || defined(NEXTSTEP) || \\
    (defined(OPENBSD) || defined(NETBSD)) && !defined(__ELF__)
#define LEADING_UNDERSCORE "_"
#else
#define LEADING_UNDERSCORE
#endif

#ifndef GGL_MOZJS_LIBNAME
#define GGL_MOZJS_LIBNAME "libmozjs.so"
#endif

static void *g_libmozjs_handler = NULL;

static bool LoadLibmozjs(const char *xpcom_file) {
  std::string dir, filename;
  if (IsAbsolutePath(xpcom_file) &&
      SplitFilePath(xpcom_file, &dir, &filename)) {
    filename = BuildFilePath(dir.c_str(), GGL_MOZJS_LIBNAME, NULL);
    g_libmozjs_handler = dlopen(filename.c_str(), RTLD_GLOBAL | RTLD_LAZY);
  }
  return g_libmozjs_handler != NULL;
}

bool LibmozjsGlueStartup() {
  nsresult rv = NS_OK;
  char xpcom_file[4096];

  static const GREVersionRange kGREVersion = {
    "1.9a", PR_TRUE,
    "1.9.0.*", PR_TRUE
  };

  rv = GRE_GetGREPathWithProperties(&kGREVersion, 1, nsnull, 0,
                                    xpcom_file, 4096);
  if (NS_FAILED(rv)) {
    LOGE("Failed to find proper Gecko Runtime Environment!");
    return false;
  }

  DLOG("XPCOM Location: %s", xpcom_file);

  if (!LoadLibmozjs(xpcom_file)) {
    LOGE("Failed to load " GGL_MOZJS_LIBNAME "!");
    return false;
  }

  // Load symbols.
  const nsDynamicFunctionLoad *syms = kMOZJSSymbols;
  while(syms->functionName) {
    std::string name =
      StringPrintf(LEADING_UNDERSCORE "%s", syms->functionName);
    NSFuncPtr old = *syms->function;
    *syms->function = (NSFuncPtr) dlsym(g_libmozjs_handler, name.c_str());
    if (*syms->function == old) {
      LOGE("Missing symbol in " GGL_MOZJS_LIBNAME ": %s", syms->functionName);
      rv = NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
    }
    ++syms;
  }

  return rv == NS_OK;
}

void LibmozjsGlueShutdown() {
  if (g_libmozjs_handler) {
    dlclose(g_libmozjs_handler);
    g_libmozjs_handler = NULL;
  }

  const nsDynamicFunctionLoad *syms = kMOZJSSymbols;
  while(syms->functionName) {
    *syms->function = NULL;
    ++syms;
  }
}

nsresult LibmozjsGlueStartupWithXPCOM() {
  return XPCOMGlueLoadXULFunctions(kMOZJSSymbols);
}

} // namespace libmozjs
} // namespace ggadget
#endif // XPCOM_GLUE
LIBMOZJS_GLUE_CC

rm -f mozjs_api_*
rm -f mozjs_sed_*
rm -f used_mozjs_*
